package smallfrench.ui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.prefs.Preferences;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileFilter;

import smallfrench.moteur.interp.EcouteurExecution;
import smallfrench.moteur.interp.Interpreteur;
import smallfrench.moteur.interp.ProcessusScript;
import smallfrench.moteur.interp.SortieConsole;
import smallfrench.moteur.interp.err.*;
import smallfrench.moteur.lexer.Position;
import smallfrench.moteur.lexer.Positionnable;

/**
 * La fenêtre principale de smallFrench
 * @author antoine1023
 *
 */
@SuppressWarnings("serial")
public class FenetrePrincipale extends JFrame implements EcouteurExecution {

	private static final String SCRIPT_DEFAUT = "/* Script smallFrench */\n\n";

	//---------------------------------------------------------------- Actions

	private FichierNouveau fichierNouveau;
	private FichierEnregistrer fichierEnregistrer;
	private FichierEnregistrerSous fichierEnregistrerSous;
	private FichierOuvrir fichierOuvrir;
	private FichierExecuter fichierExecuter;
	private FichierStopper fichierStopper;
	private FichierQuitter fichierQuitter;

	private AidePageProjet aidePageProjet;
	private AideSignalerProbleme aideSignalerProbleme;
	//	private AideRamasseMiettes aideRamasseMiettes;
	private AideAPropos aideAPropos;

	//-------------------------------------------------------------- Dialogues

	private final JFileChooser selectionneurFichier = new JFileChooser();

	private static final FileFilter FILTRE_FICHIERS = new FileFilter() {
		@Override
		public String getDescription() {
			return "Scripts smallFrench (\"*.sfrench\")";
		}

		@Override
		public boolean accept(File f) {
			if (f.isDirectory()) return true;
			String ext = getExtension(f);
			if (ext == null) return false;
			return ext.equals("sfrench");
		}
	};

	private DialogueRechercher dialogueRechercher;

	//------------------------------------------------------------- Composants

	private JPanel contentPane;

	private JToolBar barreOutils;

	private Editeur editeur;

	private Console console;
	private JSplitPane splitPane;

	private ProcessusScript processusScript;

	private Preferences preferences;

	//------------------------------------------------------------------------

	/**
	 * Crée la fenetre.
	 */
	public FenetrePrincipale() {

		super("smallFrench");
		preferences = Preferences.userNodeForPackage(FenetrePrincipale.class);
		preferences.node("fenetreprincipale");
		setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
		addWindowListener(new WindowListener() {
			@Override
			public void windowOpened(WindowEvent e) {}
			@Override
			public void windowIconified(WindowEvent e) {}
			@Override
			public void windowDeiconified(WindowEvent e) {}
			@Override
			public void windowDeactivated(WindowEvent e) {}

			@Override
			public void windowClosing(WindowEvent e) {
				fichierQuitter.actionPerformed(null);
			}

			@Override
			public void windowClosed(WindowEvent e) {}
			@Override
			public void windowActivated(WindowEvent e) {}
		});
		setMinimumSize(new Dimension(300, 200));
		setSize(preferences.getInt("taillex", 800),
				preferences.getInt("tailley", 700));
		addComponentListener(new ComponentListener() {
			@Override
			public void componentShown(ComponentEvent e) {}

			@Override
			public void componentResized(ComponentEvent e) {
				preferences.putInt("taillex", getWidth());
				preferences.putInt("tailley", getHeight());
			}

			@Override
			public void componentMoved(ComponentEvent e) {}

			@Override
			public void componentHidden(ComponentEvent e) {}
		});
		setLocationRelativeTo(null); // centrage au milieu de l’écran

		dialogueRechercher = new DialogueRechercher(this);

		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));

		//------------

		initActions();

		//------------

		editeur = new Editeur(this);

		setJMenuBar(creerBarreMenu());

		//------------

		barreOutils = new JToolBar();

		barreOutils.add(fichierNouveau);
		barreOutils.add(fichierOuvrir);
		barreOutils.add(fichierEnregistrer);
		barreOutils.addSeparator();
		barreOutils.add(fichierExecuter);
		barreOutils.add(fichierStopper);
		barreOutils.addSeparator();
		barreOutils.add(editeur.getActionDefaire());
		barreOutils.add(editeur.getActionRefaire());
		barreOutils.addSeparator();
		barreOutils.add(Actions.COUPER);
		barreOutils.add(Actions.COPIER);
		barreOutils.add(Actions.COLLER);
		barreOutils.addSeparator();
		barreOutils.add(aidePageProjet);

		contentPane.add(barreOutils, BorderLayout.NORTH);

		//------------

		editeur.setEcouteurFichier(new EcouteurFichier() {
			@Override
			public void nonEnregistre() {
				actuTitre();
			}

			@Override
			public void enregistre() {
				actuTitre();
			}
		});

		console = new Console();

		splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
				editeur.getScrollPane(), console);
		splitPane.setDividerLocation((int)(getHeight() * 0.6));

		contentPane.add(splitPane, BorderLayout.CENTER);

		setContentPane(contentPane);

		fichierNouveau.actionPerformed(null);

		dialogueRechercher.setTextPane(editeur.getTextPane());
	}

	/**
	 * On initialise les actions
	 */
	private void initActions() {

		fichierNouveau = new FichierNouveau();
		fichierOuvrir = new FichierOuvrir();
		fichierEnregistrer = new FichierEnregistrer();
		fichierEnregistrerSous = new FichierEnregistrerSous();
		fichierExecuter = new FichierExecuter();
		fichierStopper = new FichierStopper();
		fichierStopper.setEnabled(false);
		fichierQuitter = new FichierQuitter();

		aidePageProjet = new AidePageProjet(this);
		aideSignalerProbleme = new AideSignalerProbleme(this);
		//	aideRamasseMiettes = new AideRamasseMiettes();
		aideAPropos = new AideAPropos();
	}

	//---------------------------------------------------------

	/**
	 * Créé la barre de menu avec les actions.
	 * @return La barre de menu créée.
	 */
	private JMenuBar creerBarreMenu() {

		JMenu menuFichier = new JMenu("Fichier");
		menuFichier.add(fichierNouveau);
		menuFichier.add(fichierOuvrir);
		menuFichier.add(Exemples.getMenu(this));
		menuFichier.addSeparator(); // --------------
		menuFichier.add(fichierEnregistrer);
		menuFichier.add(fichierEnregistrerSous);
		menuFichier.addSeparator(); // --------------
		menuFichier.add(fichierExecuter);
		menuFichier.add(fichierStopper);
		menuFichier.addSeparator(); // --------------
		menuFichier.add(fichierQuitter);

		JMenu menuEdition = new JMenu("Édition");
		menuEdition.add(editeur.getActionDefaire());
		menuEdition.add(editeur.getActionRefaire());
		menuEdition.addSeparator(); // --------------
		menuEdition.add(Actions.COUPER);
		menuEdition.add(Actions.COPIER);
		menuEdition.add(Actions.COLLER);
		menuEdition.addSeparator(); // --------------
		menuEdition.add(Actions.TOUT_SEL);
		menuEdition.addSeparator(); // --------------
		menuEdition.add(editeur.getActionRechercher());
		menuEdition.addSeparator(); // --------------

		final JCheckBox cb = new JCheckBox("Coloration lexicale");
		cb.setSelected(editeur.getColorationLexicale());
		cb.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				editeur.setColorationLexicale(cb.isSelected());
			}
		});
		menuEdition.add(cb);

		JMenu menuAide = new JMenu("Aide");
		menuAide.add(aidePageProjet);
		menuAide.add(aideSignalerProbleme);
		// menuAide.add(aideRamasseMiettes);
		menuAide.add(aideAPropos);

		JMenuBar barre = new JMenuBar();
		barre = new JMenuBar();
		barre.add(menuFichier);
		barre.add(menuEdition);
		barre.add(menuAide);

		return barre;
	}

	//------------------

	/**
	 * @return Un fichier ou null.
	 */
	private File choisirFichierAEnregistrer() {
		initSelectionneurFichier();
		selectionneurFichier.showSaveDialog(FenetrePrincipale.this);
		return selectionneurFichier.getSelectedFile();
	}

	/**
	 * @return Un fichier ou null.
	 */
	private File choisirFichierAOuvrir() {
		initSelectionneurFichier();
		selectionneurFichier.showOpenDialog(FenetrePrincipale.this);
		return selectionneurFichier.getSelectedFile();
	}

	/**
	 * Initialise selectionneurFichier
	 * @return Le {@link JFileChooser} <code>selectionneurFichier</code>.
	 */
	private JFileChooser initSelectionneurFichier() {
		Action details = selectionneurFichier.getActionMap()
				.get("viewTypeDetails");
		details.actionPerformed(null);
		selectionneurFichier.setAcceptAllFileFilterUsed(true);
		selectionneurFichier.setMultiSelectionEnabled(false);
		selectionneurFichier.setFileSelectionMode(JFileChooser.FILES_ONLY);
		selectionneurFichier.setFileFilter(FILTRE_FICHIERS);
		return selectionneurFichier;
	}

	/**
	 * 
	 * @param f
	 * @return L’extension du fichier f ou <code>null</code>.
	 */
	public static String getExtension(File f) {
		String ext = null;
		String s = f.getName();
		int i = s.lastIndexOf('.');

		if (i > 0 &&  i < s.length() - 1) {
			ext = s.substring(i+1).toLowerCase();
		}
		return ext;
	}

	private void actuTitre() {
		File f = editeur.getFichier();
		String s = "smallFrench";
		if (f != null) {
			s += " - ";
			if (! editeur.estEnregistre()) {
				s += '*';
			}
			s += f.getName();
		}
		setTitle(s);
	}

	public final static int
	CONTINUER = 0,
	ANNULER = 1,
	ENREGISTRER = 2;

	/**
	 * Affiche une confirmation de fermeture d’un fichier,
	 * changements non sauvegardés.
	 * 
	 * @param parent Le composant parent du dialogue
	 * @return CONTINUER ou ANNULER ou ENREGISTRER
	 */
	public static int confirmerFermeture(Component parent) {

		Object[] options = {
				"Fermer sans enregistrer",
				"Annuler",
				"Enregistrer"
		};

		String msg = "Voulez-vous enregistrer les modifications " +
				"du fichier avant de le fermer ?";

		int n = JOptionPane.showOptionDialog(
				parent,
				msg,
				"smallFrench",
				JOptionPane.YES_NO_CANCEL_OPTION,
				JOptionPane.WARNING_MESSAGE,
				null,
				options,
				options[2]);

		if (n == JOptionPane.CLOSED_OPTION)
			return ANNULER;

		return n;
	}

	public void ouvrirFichierExemple(String script) {
		if (! editeur.estEnregistre()) {
			int c = confirmerFermeture(this);
			switch (c) {
			case ENREGISTRER:
				editeur.enregistrer();
				break;

			case CONTINUER:
				break;

			default:
				return;
			}
		}
		editeur.ouvrirFichierExemple(script);
	}


	public void enregistrer() {
		if (editeur.enregistrable()) {
			editeur.enregistrer();
		} else {
			fichierEnregistrerSous.actionPerformed(null);
		}
	}


	private void lancerProgramme() {

		fichierExecuter.setEnabled(false);
		fichierStopper.setEnabled(true);

		console.viderTamponEntree();
		console.reinitialiser();
		console.requestFocus(false);

		SortieConsole stderr = new SortieConsole(System.err);

		AfficheurErreur.Ecouteur ecouteur = new AfficheurErreur.Ecouteur() {
			@Override
			public void onErreur(Object erreur) {
				if (erreur instanceof Positionnable) {
					Positionnable p = (Positionnable) erreur;
					Position pos = p.getPosition();
					if (pos != null)
						surlignerLigneErreur(pos);
				}
				onFin(null);
			}
		};

		IntercepteurErreurs intercepteur = new IntercepteurErreurs();
		AfficheurErreur<?>
		affEP = new AfficheurErreurLexer(console, stderr),
		affRT = new AfficheurRException(console, stderr),
		affT = new AfficheurRuntimeException(console);

		affRT.ajouter(ecouteur);
		affEP.ajouter(ecouteur);
		affT.ajouter(ecouteur);

		intercepteur.ajouter(affRT);
		intercepteur.ajouter(affEP);
		intercepteur.ajouter(affT);

		Interpreteur interp = new Interpreteur(
				console, console, console,
				intercepteur);

		interp.ajouterEcouteur(this);

		processusScript = interp.evaluer(editeur.getTexte());

		//	if (processusScript == null) {
		//		// Une erreur s’est produite
		//		onFin(null);
		//		return;
		//	}

	}

	/**
	 * Ouvre une fenêtre "À propos de smallFrench"
	 */
	private void afficherAPropos() {
		APropos f = new APropos(this);
		f.setVisible(true);
	}

	public DialogueRechercher getDialogueRechercher() {
		return dialogueRechercher;
	}

	//----------------------------------------------------------------- Actions
	//----------------------------- Menu Fichier

	public class FichierNouveau extends AbstractAction {
		public FichierNouveau() {
			super("Nouveau", Ressources.getIcone("document-new"));
			putValue(SHORT_DESCRIPTION, "Créer un nouveau fichier");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control N"));
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			Thread t = new Thread(new Runnable() {
				@Override
				public void run() {
					ouvrirFichierExemple(SCRIPT_DEFAUT);
				}
			});
			t.start();
		}
	};

	public class FichierQuitter extends AbstractAction {
		public FichierQuitter() {
			super("Quitter");
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			if (! editeur.estEnregistre()) {
				int c = confirmerFermeture(FenetrePrincipale.this);
				switch (c) {
				case ENREGISTRER:
					enregistrer();

				case CONTINUER:
					break;

				default:
					return;
				}
				System.exit(0);
			} else {
				System.exit(0);
			}
		}
	};

	public class FichierOuvrir extends AbstractAction {
		public FichierOuvrir() {
			super("Ouvrir", Ressources.getIcone("document-open"));
			putValue(SHORT_DESCRIPTION, "Ouvrir un fichier existant");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control O"));
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			if (! editeur.estEnregistre()) {

				int c = confirmerFermeture(FenetrePrincipale.this);
				switch (c) {
				case ENREGISTRER:
					enregistrer();
					break;

				case CONTINUER:
					break;

				default:
					return;
				}
			}
			File f = choisirFichierAOuvrir();
			if (f != null && f.canRead()) {
				editeur.ouvrir(f);
			}
		}
	};

	public class FichierExecuter extends AbstractAction {
		public FichierExecuter() {
			super("Exécuter", Ressources.getIcone("play-start"));
			putValue(SHORT_DESCRIPTION, "Exécuter le fichier");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("F5"));
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			lancerProgramme();
		}
	};

	public class FichierStopper extends AbstractAction {
		public FichierStopper() {
			super("Stopper", Ressources.getIcone("play-stop"));
			putValue(SHORT_DESCRIPTION, "Arrêter l’exécution du fichier");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control F5"));
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			System.out
			.println("FenetrePrincipale.FichierStopper.actionPerformed()");
			// On tue le process
			processusScript.tuerEtSignaler();
			processusScript = null;
		}
	};

	@Override
	public void onFin(ProcessusScript ps) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {

				processusScript = null;

				fichierExecuter.setEnabled(true);
				fichierStopper.setEnabled(false);

				// On rafraichi la console
				console.repaint();
				editeur.getTextPane().requestFocus(false);

				System.out.println("Exécution terminée.");
			}
		});
	}

	private void surlignerLigneErreur(Position position) {
		MTextPane tp = editeur.getTextPane();
		tp.setCaretPosition(position.index);
		tp.setCouleurLigneErreur();
		tp.requestFocus(false);
	}

	public class FichierEnregistrer extends AbstractAction {
		public FichierEnregistrer() {
			super("Enregistrer", Ressources.getIcone("document-save"));
			putValue(SHORT_DESCRIPTION, "Sauvegarder vos modifications en " +
					"enregistrant le fichier");
			putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control S"));
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			if (editeur.enregistrable())
				editeur.enregistrer();
			else {
				File f = choisirFichierAEnregistrer();
				System.out.println(f);
				if (f != null) {
					editeur.enregistrer(f);
				}
			}
		}
	};

	public class FichierEnregistrerSous extends AbstractAction {
		public FichierEnregistrerSous() {
			super("Enregistrer sous...",
					Ressources.getIcone("document-save-as"));

			putValue(SHORT_DESCRIPTION, "Enregistrer le fichier sous un " +
					"autre emplacement");
			putValue(ACCELERATOR_KEY,
					KeyStroke.getKeyStroke("shift control S"));
		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			File f = choisirFichierAEnregistrer();
			System.out.println(f);
			if (f != null) {
				editeur.enregistrer(f);
			}
		}
	};



	//----------------------------- Menu Aide

	public class AidePageProjet extends AbstractAction {
		public AidePageProjet(FenetrePrincipale fp) {
			super("Page du projet sur Google Code",
					Ressources.getIcone("help"));
			putValue(SHORT_DESCRIPTION, "Page du projet sur Google Code");
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			try {
				Desktop.getDesktop().browse(new URI(
						"https://code.google.com/p/smallfrench/"));
			} catch (IOException e0) {
				e0.printStackTrace();
			} catch (URISyntaxException e1) {
				e1.printStackTrace();
			}
		}
	};

	public class AideSignalerProbleme extends AbstractAction {
		public AideSignalerProbleme(FenetrePrincipale fp) {
			String nom = "Signaler un problème ou suggérer une amélioration";
			putValue(NAME, nom);
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			String url = "http://code.google.com/p/smallfrench/wiki/" +
					"RapportProbleme?tm=3";
			try {
				Desktop.getDesktop().browse(new URI(url));
			} catch (IOException e0) {
				e0.printStackTrace();
			} catch (URISyntaxException e1) {
				e1.printStackTrace();
			}
		}
	};

	//	public class AideRamasseMiettes extends AbstractAction {
	//		public AideRamasseMiettes() {
	//			super("Ramasse-miettes");
	//			putValue(SHORT_DESCRIPTION, "Le ramasse-miettes (garbage " +
	//					"collector en anglais) permet de libérer de " +
	//					"la mémoire vive en " +
	//					"détruisant des objets préalablement alloués puis " +
	//					"inutilisés.");
	//
	//		}
	//		@Override
	//		public void actionPerformed(ActionEvent e) {
	//			System.gc();
	//		}
	//	};

	public class AideAPropos extends AbstractAction {

		public AideAPropos() {
			super("À propos de smallFrench");
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			afficherAPropos();
		}
	};

}

